package qc.common.core.utils;

import qc.common.core.enums.TimeRangeLengthEnum;
import qc.common.core.exception.QCPromptException;

import java.util.Date;

/**
 * 时间段处理工具类，用于计算时段的起止时间、上一/下一时段时间
 *
 * @author QuCheng Tech
 * @since 2024/9/30
 */
public class TimeSpanUtil {
    /**
     * 获取时段起始时间，如果传入的时间点就是分界点则返回该时间点，一天从0点0分开始
     *
     * @param date            时间点
     * @param intervalMinutes 时段长分钟数
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2024/9/30
     */
    public static Date getBeginTime(Date date, int intervalMinutes) {
        return getBeginTime(date, intervalMinutes, 0x0);
    }

    /**
     * 获取时段起始时间，如果传入的时间点就是分界点则返回该时间点
     *
     * @param date            时间点
     * @param intervalMinutes 时段长分钟数
     * @param dayBeginMinutes 一天的起始点分钟数，距0点0分的分钟数
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2024/9/30
     */
    public static Date getBeginTime(Date date, int intervalMinutes, int dayBeginMinutes) {
        Date dayTime = DateUtil.getDayDate(date);
        //传入时间精确到日期、加上小时数
        Date nonSecondsTime = DateUtil.addHours(dayTime, DateUtil.getHour(date));
        //传入时间精确到日期、加上分钟数
        nonSecondsTime = DateUtil.addMinutes(nonSecondsTime, DateUtil.getMinute(date));
        //如果传入的时段长分钟数小于等于1，直接返回传入的时间
        if (intervalMinutes <= 0x1)//返回的时间精确到分钟，设置秒为0
            return nonSecondsTime;

        //如果一天的起始时间分钟数大于0，加上起始分钟数；如设置一天的起始时间为8点，传入的起始分钟数为480
        if (dayBeginMinutes > 0x0) {
            dayTime = DateUtil.addMinutes(dayTime, dayBeginMinutes);
            //如果加上起始分钟数后时间大于传入的时间，需要将日期往前1天
            if (dayTime.after(nonSecondsTime))
                dayTime = DateUtil.addDays(dayTime, -1);
        }

        //计算传入的date距一天的开始时间共有多少分钟，按照传入的时段分钟数取模运算
        int totalMinutes = (int) DateUtil.getDistanceMinutes(dayTime, nonSecondsTime);
        int intervals = (totalMinutes - (totalMinutes % intervalMinutes)) / intervalMinutes;
        //计算时段的开始时间点
        Date hourBeginTime = DateUtil.addMinutes(dayTime, intervals * intervalMinutes);
        //只有2种情况：时段开始时间点就是传入的时间，时段开始时间点在传入的时间点之前
        //计算的时段开始时间点就是要返回的时段起始时间
        return hourBeginTime;
    }

    /**
     * 获取时段截止时间，如果传入的时间点就是分界点则返回该时间点，一天从0点0分开始
     *
     * @param date            时间点
     * @param intervalMinutes 时段长分钟数
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2024/9/30
     */
    public static Date getEndTime(Date date, int intervalMinutes) {
        return getEndTime(date, intervalMinutes, 0x0);
    }

    /**
     * 获取时段截止时间，如果传入的时间点就是分界点则返回该时间点
     *
     * @param date            时间点
     * @param intervalMinutes 时段长分钟数
     * @param dayBeginMinutes 一天的起始点分钟数，距0点0分的分钟数
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2024/9/30
     */
    public static Date getEndTime(Date date, int intervalMinutes, int dayBeginMinutes) {
        //如果传入的时段长分钟数小于等于0，直接返回传入的时间
        //如果传入的时段长分钟数等于1，需要判断传入时间点秒数，如果秒数为0返回传入时间点，如果秒数大于0返回下一分钟
        if (intervalMinutes <= 0x0)
            return date;

        //根据传入时间点获取时段的起始时间点
        Date hourBeginTime = getBeginTime(date, intervalMinutes, dayBeginMinutes);
        //时段起始时间就是传入的时间点，传入的时间点是分界点时间（即作为时段末时间点）
        //时段起始时间在传入的时间点之前，加上时段间隔分钟数就是时段截止时间
        if (hourBeginTime.before(date))
            return DateUtil.addMinutes(hourBeginTime, intervalMinutes);
        else
            return hourBeginTime;
    }

    /**
     * 获取指定时段长度的起始时间，默认日数据的起始时间为0点0分
     *
     * @param date   指定的时间点
     * @param tmtype 时段长度类型枚举
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2025/5/19
     */
    public static Date getBeginTime(Date date, TimeRangeLengthEnum tmtype) throws QCPromptException {
        return getBeginTime(date, tmtype, 0x0, 0x0);//默认间隔为0，日数据的起始时间为0点0分
    }

    /**
     * 获取指定时段长度的起始时间，默认日数据的起始时间为0点0分
     *
     * @param date     指定的时间点
     * @param tmtype   时段长度类型枚举
     * @param interval 间隔数，在时段长度类型枚举为分钟时有效
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2025/5/19
     */
    public static Date getBeginTime(Date date, TimeRangeLengthEnum tmtype, int interval) throws QCPromptException {
        return getBeginTime(date, tmtype, interval, 0x0);//默认日数据的起始时间为0点0分
    }

    /**
     * 获取指定时段长度的起始时间
     *
     * @param date            指定的时间点
     * @param tmtype          时段长度类型枚举
     * @param interval        间隔数，在时段长度类型枚举为分钟时有效
     * @param dayBeginMinutes 日起始时间（从0点0分开始的分钟数，如日起始为8点设置为480），在时段长度类型为天及以上时有效
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2025/5/19
     */
    public static Date getBeginTime(Date date, TimeRangeLengthEnum tmtype, int interval, int dayBeginMinutes) throws QCPromptException {
        Date result = date;
        //根据时段类型长度枚举进行不同处理
        switch (tmtype) {
            case HOUR:
                break;
            case DAY:
                //日数据先保留到0点0分，在加上指定的日起始分钟数
                result = DateUtil.getDayDate(date);
                if (dayBeginMinutes > 0x0)
                    result = DateUtil.addMinutes(result, dayBeginMinutes);

                return result;
            case WEEK:
                break;
            case TEN_DAYS:
                break;
            case MONTH:
                //月数据先保留到月份的1号0点0分，在加上指定的日起始分钟数
                result = DateUtil.newDate(DateUtil.getYear(date), DateUtil.getMonthFromZero(date), 0x1, true);
                if (dayBeginMinutes > 0x0)
                    result = DateUtil.addMinutes(result, dayBeginMinutes);

                return result;
            case QUARTER:
                break;
            case YEAR:
                //月数据先保留到年份的1月1日0点0分，在加上指定的日起始分钟数
                result = DateUtil.newDate(DateUtil.getYear(date), 0x1, 0x1, false);
                if (dayBeginMinutes > 0x0)
                    result = DateUtil.addMinutes(result, dayBeginMinutes);

                return result;
            default://其他均当做以指定的分钟数为间隔长度
                //时段长度类型为分钟，判断间隔是否有效，如果无效抛出异常提示
                if (interval < 0x1)
                    throw new QCPromptException("TimeSpanUtil计算时段起始时间，时段类型为分钟时指定的间隔分钟数必须大于0");
                break;
        }

        //其他情况下返回输入的时间点
        return date;
    }
}
